'use strict';

var classBuilder = require('../util/classBuilder'),

    // Registry to register all the task types
    typeRegistry = {},

    // Registers a task
    registerTaskType = function (type, ctor) {
        typeRegistry[type] = ctor;

        console.log('Registered type: ' + type);
    },

    // Creates a task from the registered task types
    createTask = function (conf) {
        if (!conf.type) {
            throw 'Task must have a type';
        }

        var ctor = typeRegistry[conf.type];

        if (!ctor) {
            throw 'Must include module for type: ' + conf.type;
        }

        return new ctor(conf);
    },

    // Define the base task
    Task = classBuilder.define({

        construct: function (conf) {

            /* Required */
            // The id of this task
            this.id = conf.id;

            // The results of executing task
            this.results = {};

            // The error result if any
            this.error = null;

            /* Required */
            // execute(params, error)
            // The method that gets executed
            // params, the parameters to use
            // error, the error from previous task
            this.execute = conf.execute;

            // The method to route to the next task
            this.route = conf.route;
        },

        /* Required */
        // The type of this task, needs to be overriden by derived classes
        type: 'task',

        // Whether the task is being executed asynchronously (in callbacks) or synchronously.
        // Asynchronous tasks need to set up the results and errors inside the callback and call next() (also inside the callbacks)
        async: false,

        // Runs the next task if a route was provided
        next: function () {
            if (this.route) {
                console.log('Routing to next task ...');

                var taskId = this.route(),
                    task = this.runner.getTask(taskId);

                task.run(this.results, this.error);
            }
        },

        run: function (params, error) {
            console.log('Task started. type: ' + this.type + ' id: ' + this.id);

            this.results = null; // Reset the results
            this.error = null; // Reset the error

            try {
                this.results = this.execute(params, error);
            }
            catch (err) {
                this.error = err;
            }

            console.log('Task ended. type: ' + this.type + ' id: ' + this.id);

            if (!this.async) { // Asynchronous tasks need to call the next() function inside their callbacks
                this.next(); 
            }
        }
    });

// Register this task type
registerTaskType(Task.prototype.type, Task);

Task.registerType = registerTaskType;
Task.create = createTask;

exports.Task = Task;
